/**
  * @file       queue.c
  * @author     const_zpc (any question please send mail to const_zpc@163.com)
  * @brief      定长的队列功能实现【FIFO】
  * @version    V0.2
  * @date       2023-05-29
  * 
  * 
  * ********************************************************************************************************************
  * @details  功能详细说明：
  *           + 元素添加
  *                 + cotQueue_Push(cotQueue_t *pQueue, const void *pdata, size_t length)
  *           + 元素移除
  *                 + cotQueue_Pop(cotQueue_t *pQueue)
  *           + 元素访问
  *                 + cotQueue_Front(cotQueue_t *pQueue)
  *                 + cotQueue_Back(cotQueue_t *pQueue)
  *           + 元素容量
  *                 + cotQueue_Empty(cotQueue_t *pQueue)
  *                 + cotQueue_Full(cotQueue_t *pQueue)
  *                 + cotQueue_Size(cotQueue_t *pQueue)
  *           + 内存交换（队列内存交换，减少内存拷贝）
  *                 + cotQueue_Swap(cotQueue_t *pQueue1, cotQueue_t *pQueue2)
  * 
  * ********************************************************************************************************************
  * @par 源码路径: https://gitee.com/const-zpc/cot.git 具体问题及建议可在该网址填写 Issue
  * @par 修改日志: 
  * <table>
  * <tr><th>Date           <th>Version   <th>Author      <th>Description
  * <tr><td>2023-03-26     <td>1.0       <td>const_zpc       <td>初版
  * </table>
  * ********************************************************************************************************************
  */

/* Includes ----------------------------------------------------------------------------------------------------------*/
#include "container/queue.h"
#include <string.h>

/**
  * @brief      队列初始化
  * 
  * @note       会根据buf大小和元素的大小限制自动计算得到队列的限制数目
  * @param      pQueue 队列句柄
  * @param      pBuf   队列使用的buf
  * @param      bufSize 队列使用的buf大小
  * @param      itemSize 队列每个元素的大小限制
  */
void cotQueue_Init(cotQueue_t *pQueue, uint8_t *pBuf, size_t bufSize, size_t itemSize)
{
    pQueue->pBuf = pBuf;
    pQueue->itemSize = itemSize;
    pQueue->limit = bufSize / itemSize;
    pQueue->rIdx = 0;
    pQueue->wIdx = 0;
    pQueue->isEmpty = true;
}

/**
  * @brief      判断队列是否为空
  * 
  * @param      pQueue 队列句柄
  * @return     true   空
  * @return     false  非空
  */
bool cotQueue_Empty(cotQueue_t *pQueue)
{
    return pQueue->isEmpty;
}

/**
  * @brief      判断队列是否满了
  * 
  * @param      pQueue 队列句柄
  * @return     true  已满
  * @return     false 未满
  */
bool cotQueue_Full(cotQueue_t *pQueue)
{
    if (pQueue->isEmpty || pQueue->rIdx != pQueue->wIdx)
    {
        return false;
    }

    return true;
}

/**
  * @brief      获取队列元素数目
  * 
  * @param      pQueue 队列句柄
  * @return     元素数目 
  */
size_t cotQueue_Size(cotQueue_t *pQueue)
{
    if (pQueue->wIdx > pQueue->rIdx)
    {
        return pQueue->wIdx - pQueue->rIdx;
    }
    else
    {
        if (!pQueue->isEmpty)
        {
            return pQueue->limit - pQueue->rIdx + pQueue->wIdx;
        }
    }
    
    return 0;
}

/**
  * @brief      返回队列头部元素指针
  * 
  * @param      pQueue 队列句柄
  * @return     void* 
  */
void *cotQueue_Front(cotQueue_t *pQueue)
{
    if (!pQueue->isEmpty)
    {
        return &pQueue->pBuf[pQueue->itemSize * pQueue->rIdx];
    }
    
    return NULL;
}

/**
  * @brief      返回队列尾部元素指针
  * 
  * @param      pQueue 队列句柄
  * @return     void* 
  */
void *cotQueue_Back(cotQueue_t *pQueue)
{
    if (!pQueue->isEmpty)
    {
        if (pQueue->wIdx == 0)
        {
            return &pQueue->pBuf[pQueue->itemSize * (pQueue->limit - 1)];
        }
        
        return &pQueue->pBuf[pQueue->itemSize * (pQueue->wIdx - 1)];
    }
    
    return NULL;
}

/**
  * @brief      推送数据到队列尾部
  * 
  * @param      pQueue  队列句柄
  * @param      pdata   数据内容
  * @param      length  数据长度, 小于等于初始化设置的元素大小
  * @return     int 
  */
int cotQueue_Push(cotQueue_t *pQueue, const void *pdata, size_t length)
{
    if (length > pQueue->itemSize)
    {
        return -1;
    }
    
    if (cotQueue_Size(pQueue) >= pQueue->limit)
    {
        return -1;
    }
    
    memcpy(&pQueue->pBuf[pQueue->itemSize * pQueue->wIdx], pdata, length < pQueue->itemSize ? length : pQueue->itemSize);
    pQueue->wIdx = (pQueue->wIdx + 1) >= pQueue->limit ? 0 : pQueue->wIdx + 1;
    pQueue->isEmpty = false;
    return 0;
}

/**
  * @brief      弹出队列头元素
  * 
  * @param      pQueue 队列句柄
  * @return     int 
  */
int cotQueue_Pop(cotQueue_t *pQueue)
{
    if (cotQueue_Size(pQueue) == 0)
    {
        return -1;
    }
    
    pQueue->rIdx = (pQueue->rIdx + 1) >= pQueue->limit ? 0 : pQueue->rIdx + 1;
    pQueue->isEmpty = pQueue->rIdx == pQueue->wIdx ? true : false ;
    return 0;
}

/**
  * @brief      两个队列内存进行交换
  * 
  * @param      pQueue1  需要交换的队列句柄1
  * @param      pQueue2  需要交换的队列句柄2
  */
void cotQueue_Swap(cotQueue_t *pQueue1, cotQueue_t *pQueue2)
{
    cotQueue_t pTmpQueue = *pQueue2;

    *pQueue2 = *pQueue1;
    *pQueue1 = pTmpQueue;
}
